package dht;

import java.io.*;
import java.net.*;
import java.util.List;
import java.util.Random;

public class WebServer {

    static Random rng = new Random();

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(Integer.parseInt(args[0]));
            for (;;) {
                Socket client = serverSocket.accept();
                PrintWriter out = new PrintWriter(client.getOutputStream(), true);
                BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
                String request = in.readLine();
                String output = "";
                try {
                    if (request.length() > 0 && !request.contains("favicon.ico") && !request.equalsIgnoreCase("GET / HTTP/1.1"))
                        if (request.contains("option=put")) {
                            String idText = request.split("ID=")[1].split("&")[0];
                            int id = idText.length() == 0 ? 0 : Integer.parseInt(idText);
                            String periodText = request.split("Period=")[1].split("&")[0];
                            double period = periodText.length() == 0 ? 0 : Double.parseDouble(periodText);
                            String magnitudeText = request.split("Magnitude=")[1].split("&")[0];
                            double magnitude = periodText.length() == 0 ? 0 : Double.parseDouble(magnitudeText);
                            if (period < 0 || magnitude < 0) output = "Error: Period and magnitude must be non-negative.";
                            else output = update(id, magnitude, period);
                        } else if (request.contains("option=get")) {
                            String type = "", compare = "";
                            if (request.contains("getGroup=ID")) type = "id";
                            else if (request.contains("getGroup=Period")) type = "period";
                            else if (request.contains("getGroup=Magnitude")) type = "magnitude";
                            if (request.contains("operatorGroup=lt")) compare = "lt";
                            else if (request.contains("operatorGroup=gt")) compare = "gt";
                            else if (request.contains("operatorGroup=average")) compare = "average";
                            String gValue = request.split("gValue=")[1].split(" ")[0];
                            double value = gValue.length() == 0 ? 0 : Double.parseDouble(gValue);
                            System.out.println("Getting " + type + " " + compare + " " + value);
                            output = get(type, compare, value);
                        } else output = "Error: Please select 'put' or 'get'.";
                } catch (NumberFormatException ex) {
                    System.err.println(ex);
                    output = "Error: Please input only numbers.";
                }
                String reply = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n  <head>\n    <META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n	<title>CSC445 Stellar Database</title>\n	<style type=\"text/css\">\n      td { text-align: center; width: 33%; }\n	</style>\n  </head>\n<body>\n  <div>\n    <div bgcolor=\"#669966\" align=\"center\">\n      <h2>Stellar Database</h2>\n      <form method=\"get\">\n        <input type=\"radio\" value=\"put\" name=\"option\"> <strong>Put</strong> \n        <table border=\"2\" style=\"background-color:#ffffcc\" width=\"400\" cellpadding=\"3\" cellspacing=\"3\">\n          <tr>\n            <td>ID: <input name=\"pID\" type=\"number\" min=\"1\"></td>\n            <td>Period: <input name=\"pPeriod\" type=\"text\"></td>\n            <td>Magnitude: <input name=\"pMagnitude\" type=\"text\"></td>\n          </tr>\n        </table>\n        <br><br>\n        <input type=\"radio\" value=\"get\" name=\"option\"> <strong>Get</strong>\n        <table border=\"2\" style=\"background-color:#ffffcc\" width=\"400\" cellpadding=\"3\" cellspacing=\"3\">\n          <tr>\n            <td><input type=\"radio\" value=\"ID\" name=\"getGroup\"> ID</td>\n            <td><input type=\"radio\" value=\"Period\" name=\"getGroup\"> Period</td>\n            <td><input type=\"radio\" value=\"Magnitude\" name=\"getGroup\"> Magnitude</td>\n          </tr>\n          <tr>\n            <td><input type=\"radio\" value=\"gt\" name=\"operatorGroup\"> &gt;</td>\n            <td><input type=\"radio\" value=\"lt\" name=\"operatorGroup\"> &lt;</td>\n			<td colspan=\"2\" rowspan=\"2\"><input type=\"radio\" value=\"average\" name=\"operatorGroup\"> Average</td>\n          </tr>\n          <tr>\n		    <td colspan=\"2\" align=\"center\"><input name=\"gValue\" type=\"text\"></td>\n          </tr>\n        </table>\n        <p style=\"font-family:verdana,arial,sans-serif;font-size:10px\">\n          <input type=\"submit\"> \n          <INPUT type=\"reset\">\n        </p>\n      </form>\n    <br/><pre>" + output + "</pre></div>\n  </body>\n</html>";
                int len = reply.length();
                out.println("HTTP/1.0 200 OK");
                out.println("Content-Length: " + len);
                out.println("Content-Type: text/html\n");
                out.println(reply);
                out.close();
                in.close();
                client.close();
            }
        } catch (BindException ex) {
            System.err.println(ex);
            ex.printStackTrace();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex1) {
                System.err.println(ex1);
                ex1.printStackTrace();
            }
        } catch (Exception ex) {
            System.err.println(ex);
            ex.printStackTrace();
        }
    }

    public static String get(String type, String compare, double value) {
        for (;;) {
            Socket socket = connect();
            ObjectOutputStream oos = null;
            ObjectInputStream ois = null;
            String response = "";
            try {
                oos = new ObjectOutputStream(socket.getOutputStream());
                ois = new ObjectInputStream(socket.getInputStream());
                System.out.println("Writing query request");
                oos.writeObject(new GetRequest(type, compare, value));
                System.out.println("Finished writing");
                response = ((GetResponse) ois.readObject()).response;
                System.out.println("Got response");
            } catch (Exception ex) {
                System.err.println(ex);
                ex.printStackTrace();
                continue;
            } finally {
                try {
                    oos.close();
                    ois.close();
                    socket.close();
                } catch (Exception ex) {
                    System.err.println(ex);
                    ex.printStackTrace();
                }
            }
            return response;
        }
    }

    public static String update(int StarID, double magnitude, double period) {
        for (;;) {
            Socket socket = connect();
            if (socket == null) {
                System.err.println("Could not establish a connection.");
                System.exit(1);
            }
            ObjectOutputStream oos = null;
            ObjectInputStream ois = null;
            PutResponse response;
            try {
                oos = new ObjectOutputStream(socket.getOutputStream());
                ois = new ObjectInputStream(socket.getInputStream());
                System.out.println("Writing star");
                oos.writeObject(new PutRequest(new Star(StarID, magnitude, period)));
                System.out.println("Finished writing");
                response = ((PutResponse) ois.readObject());
                System.out.println("Got response");
            } catch (Exception ex) {
                System.err.println(ex);
                ex.printStackTrace();
                continue;
            } finally {
                try {
                    oos.close();
                    ois.close();
                    socket.close();
                } catch (Exception ex) {
                    System.err.println(ex);
                    ex.printStackTrace();
                }
            }
            if (response.c.equals(Status.Worked)) return "Update successful.";
            else if (response.c.equals(Status.WriteError)) return "Error writing to disk.";
            else if (response.c.equals(Status.InvalidRangeError)) return "Periods and magnitudes must be positive and IDs must be continuous.";
            else return "How did this happen?";
        }
    }

    public static Socket connect() {
        System.out.println("Trying to connect");
        List<HostAddress> hosts = null;
        try {
            hosts = Configuration.getAllHosts("");
        } catch (UnknownHostException ex) {
            System.err.println(ex);
            ex.printStackTrace();
        }
        for (HostAddress h : hosts) {
            System.out.println(h.address());
        }
        Socket socket = new Socket();
        while (hosts != null && hosts.size() > 0) {
            int index = rng.nextInt(hosts.size());
            HostAddress host = hosts.get(index);
            System.out.println("Establishing a connection with " + host.hostName());
            try {
                socket.connect(new InetSocketAddress(host.address(), Configuration.TCP_SERVER_PORT), 10000);
                socket.setSoTimeout(10000);
                System.out.println("Made socket");
                return socket;
            } catch (IOException ex) {
                System.err.println(ex);
                ex.printStackTrace();
                hosts.remove(index);
                continue;
            }
        }
        return null;
    }
}
